# file:: game_setup.rb
# author::  Ralph M. Churchill
# version::
# date::
#
# This source code copyright (C) 2006 by Ralph M. Churchill
# All rights reserved.
#
# Released under the terms of the GNU General Public License
# See LICENSE file for additional information.

require 'database/game_database'
require 'models/account'
require 'models/character'
require 'models/item'
require 'models/portal'
require 'models/region'
require 'models/room'

# TODO maybe make this a class, initialized with "root dir"
# and add a helper routine to verify the appropriate directory
# structure exists UNDER root_dir. The engine could have an
# instance of this class, rather than mixing it in.
module GameDatabaseSetup
=begin
ROOT/# {{{
    accounts.yaml
    game.yaml
    players/
        PLAYER...
    regions/
        REGION.../
            characters.yaml
            items.yaml
            portals.yaml
            region.yaml
            rooms.yaml
    templates/
        characters.yaml
        items.yaml# }}}
=end
    require 'yaml'
    require 'fileutils'
    require 'util/dir_strings'

    # [+root_dir+] Root Directory, under which game data will be saved
    # [+return+] The Game loaded from disk
    def load_the_game(root_dir)
        load_accounts(root_dir)
        load_templates(root_dir)
        load_players(root_dir)
        Dir.glob(root_dir/'regions'/'*') do |region_dir|
            load_region(region_dir)
        end
        load_game(root_dir)
    end

    # [+game+] The Game to save
    # [+root_dir+] Root Directory, under which game data will be saved
    def save_the_game(game,root_dir)
        RegionDatabase.instance.each do |region|
            save_region(region,root_dir)
        end
        save_players(root_dir)
        save_templates(root_dir)
        save_accounts(root_dir)
        save_game(game,root_dir)
    end
private
    def load_game(root_dir)
        game = nil
        undump({:from => [root_dir,'game.yaml']}) do |g|
            game = g
        end
        game
    end
    def load_accounts(root_dir)
        undump({:from => [root_dir,'accounts.yaml'],
               :into => AccountDatabase.instance})
    end
    def load_templates(root_dir)
        undump({:from => [root_dir,'templates','items.yaml']}) do |its|
            ItemDatabase.instance.use_templates(its)
        end
        undump({:from => [root_dir,'templates','characters.yaml']}) do |cts|
            CharacterDatabase.instance.use_templates(cts)
        end
    end
    def load_players(root_dir)
        Dir.glob(root_dir/'players'/'*') do |file|
            if file =~ /\.items\./
                undump({:from => file}) do |player_items|
                    player_items.each do |player_item|
                        ItemDatabase.instance.add_instance(player_item)
                    end
                end
            else
                undump({:from => file}) do |player|
                    CharacterDatabase.instance.add_instance(player)
                end
            end
        end
    end
    def load_region(region_dir)
        region_name = region_dir[region_dir.rindex(File::SEPARATOR)+1..-1]
        undump({:from => [region_dir,"#{region_name}.yaml"],
               :into => RegionDatabase.instance})
        undump({:from =>[region_dir,'characters.yaml']}) do |char_list|
            char_list.each do |char|
                CharacterDatabase.instance.add_instance(char)
            end
        end
        undump({:from =>[region_dir,'items.yaml']}) do |item_list|
            item_list.each do |item|
                ItemDatabase.instance.add_instance(item)
            end
        end
        undump({:from =>[region_dir,'rooms.yaml'],
              :into => RoomDatabase.instance}) 
        undump({:from =>[region_dir,'portals.yaml'],
              :into => PortalDatabase.instance}) 
    end
    def save_game(game,root_dir)
        dump({:what => game,
             :where => [root_dir,'game.yaml']})
    end
    def save_accounts(root_dir)
        dump({:what => AccountDatabase.instance.to_a,
             :where => [root_dir,'accounts.yaml']})
    end

    def save_templates(root_dir)
        dump({:what => ItemDatabase.instance.templates.to_a,
             :where => [root_dir,'templates','items.yaml']})

        dump({:what => CharacterDatabase.instance.templates.to_a,
             :where => [root_dir,'templates','characters.yaml']})
    end
    def save_region(region,root_dir)
        region_dir = root_dir/'regions'/region.disk_name
        FileUtils.mkdir region_dir unless File.exists? region_dir

        dump({:what => region,
             :where => [region_dir,"#{region.disk_name}.yaml"] })
        dump({:what => region.characters.select{|char| not char.player?},
             :where => [region_dir,'characters.yaml']})
        dump({:what => region.items,
             :where => [region_dir,'items.yaml']})
        dump({:what => region.rooms,
             :where => [region_dir,'rooms.yaml']})
        dump({:what => region.portals,
             :where => [region_dir,'portals.yaml']})
    end
    def save_players(root_dir)
        CharacterDatabase.instance.each do |char|
            dump({:what => char,
                 :where => [root_dir,'players',"#{char.name}.yaml"]
            }) if char.player?
            dump({:what => char.items,
                 :where => [root_dir,'players',"#{char.name}.items.yaml"]
            }) if char.player?
        end
    end
    
    # [+opts+] 
    #   Valid options are
    #   :what - the object to be dumped
    #   :where - array of path info, will attempt to concatenate w/ File:SEPARATOR
    def dump(opts={})
        raise "Missing Parameters!" unless opts.key?(:what) and opts.key?(:where)
        where = opts[:where]
        where = where.join(File::SEPARATOR) if where.respond_to?(:join)
        what = opts[:what]
        # what = what.to_a if what.respond_to?(:to_a)
        File.open(where,'w') do |file|
            YAML::dump(what,file)
        end
    end

    # [+opts+]
    #   Valid options are
    #       :from - from which file do you wish to load, will attempt to
    #           concatenate w/ File:SEPARATOR
    #       :into - into which data structure do you wish to load
    # [+blk+]  Data loaded from :from will be yielded if a block is given
    def undump(opts={})
        raise "Missing Parameters!" unless opts.key?(:from)
        raise "Need a target!" unless opts.key?(:into) or block_given?
        from = opts[:from]
        from = from.join(File::SEPARATOR) if from.respond_to?(:join)
        into = opts[:into]

        File.open(from,'r') do |file|
            c = YAML::load(file)
            if block_given?
                yield c
            else 
                if c.respond_to?(:each) then
                    c.each{|cc| into.add(cc)}
                else
                    into.add(c)
                end
            end
        end if File.exists?(from)
    end
end
